import processing.net.*;

class ImageData {

  private final float UPDATE_INTERVAL = 5000;
  private float timeSinceLastUpdate = 0.0;
  private OutputStream imageFile;
  private Client dataSocket, fileSocket;
  private HashMap entries = new HashMap();
  private ArrayList downloadedEntries = new ArrayList();
  private String data = "";
  private boolean gettingFileData = false;
  private boolean gettingData = false, downloading = false;
  private int bytesRead = 0, bytesToRead = 0;
  private String fileToDownload = "";
  
  private LinkedList<Integer> toDownload = new LinkedList<Integer>();

  public ImageData()
  {
    requestAllData();
  }

  public ImageEntry getEntry(int id)
  {
    return (ImageEntry)entries.get(id);
  }
  
  public boolean exists(int id)
  {
    return entries.containsKey(id);
  }

  public void rate(int id, int video, int audio, int total)
  {
    Client sendSocket;
    sendSocket = new Client(parent, "goodman.se", 80); 
    sendSocket.write("GET /rateme/rate.php?id="+id+"&video="+video+"&audio="+audio+"&total="+total+" HTTP/1.1\n"); 
    sendSocket.write("Host: goodman.se\n\n");
    ImageEntry e = (ImageEntry)entries.get(id);
    e.setRateAudio("" + audio);
    e.setRateVideo("" + video);
    e.setRateTotal("" + total);
    e.viewCount++;  
    
 
  }
   
  public void requestAllData()
  {    
    dataSocket = new Client(parent, "goodman.se", 80); 
    dataSocket.write("GET /rateme/get.php?mode=all HTTP/1.1\n"); 
    dataSocket.write("Host: goodman.se\n\n");
  }

  public void requestNewData()
  {    
    dataSocket = new Client(parent, "goodman.se", 80); 
    dataSocket.write("GET /rateme/get.php?mode=new HTTP/1.1\n"); 
    dataSocket.write("Host: goodman.se\n\n");
  }

  public void requestSpecificData(int id)
  {    
    dataSocket = new Client(parent, "goodman.se", 80); 
    dataSocket.write("GET /rateme/get.php?mode=" + id + " HTTP/1.1\n"); 
    dataSocket.write("Host: goodman.se\n\n");
  }

  public boolean requestFile(String filename)
  {
    println("Trying to download " + filename);

    if(!downloading) {     
      downloading = true;      
      fileSocket = new Client(parent, "goodman.se", 80); 
      fileSocket.write("GET /rateme/images/"+filename+" HTTP/1.1\n"); 
      fileSocket.write("Host: goodman.se\n\n"); 
      fileToDownload = filename;      

      return true;
    }
    return false;
  }

  public boolean requestFile(int id)
  {
    if(entries.containsKey(id)) {
      ImageEntry e = (ImageEntry)entries.get(id);
      return requestFile(e.fileName);
    }
    return false;
  }
  
  public boolean hasUnrated()
  {    
    Iterator it = entries.entrySet().iterator();
    while(it.hasNext()) {
      Map.Entry entry = (Map.Entry)it.next();
      if (((ImageEntry)entry.getValue()).viewCount == 0)
        return true;
    } 

    return false;
  }

  public void handleData()
  {
    timeSinceLastUpdate += dt;

    if(timeSinceLastUpdate > UPDATE_INTERVAL) {    
      requestAllData();
      timeSinceLastUpdate = 0.0;
    }

    if(!downloading)
    {
      Integer id = toDownload.peek();
      if(id != null)
      {
        ImageEntry e = (ImageEntry)entries.get(id);      
        requestFile(e.fileName);
      }
    }

    receiveData();
    receiveFile();
  }

  public String getImageFileName(int index)
  {
    return ((ImageEntry)downloadedEntries.get(index)).fileName;
  }
  
  public ImageEntry getImageEntry(String filename)
  {
    Iterator it = entries.entrySet().iterator();
    while(it.hasNext()) {
      Map.Entry entry = (Map.Entry)it.next();
      if (((ImageEntry)entry.getValue()).fileName.equals(filename)) {
        return (ImageEntry)entry.getValue();
      } 
    }

    return null;
  }
  
  public ImageEntry getImageEntry(int index)
  {
    if (downloadedEntries.size() > 0)
      return ((ImageEntry)downloadedEntries.get(index));
    else
      return null;
  }

  public int numberOfImages()
  {
    return downloadedEntries.size();
  }

  public ImageEntry getLeastRated(int index) // STUPID, DO NOT WORK FOR 1 MILLION BILLION PICTURES
  {
    Collections.sort(downloadedEntries, new ImageEntryComparator());
    println(downloadedEntries);
    println("#####################");
    println(entries);
    ImageEntry ent = (ImageEntry)downloadedEntries.get(index);
    println("VOTING ON: " + ent.id + " VIEW COUNT: " + ent.viewCount);
    return ent;
  }

  public String toString()
  {
    String string = " ";

    Iterator it = entries.entrySet().iterator();

    while(it.hasNext()) {
      Map.Entry entry = (Map.Entry)it.next();
      string += (ImageEntry)entry.getValue() + "\n***\n";
    } 

    return string;
  }

  private void receiveFile()
  {
    if(fileSocket == null)
      return;

    if (fileSocket.available() > 0) {

      if(!gettingFileData)
      {
        String answer = fileSocket.readStringUntil('\n');

        //print(answer);

        String[] response = splitTokens(answer, " \r\n");

        if(response.length == 0)
        {
          println("Starting Download " + fileToDownload + "!");          
          imageFile = createOutput(props.getProperty("absoluteDataPath") + fileToDownload);
          gettingFileData = true;
        }
        else if(response.length > 1 && response[0].equals("HTTP/1.1") && response[1].equals("200"))
        {
          //println("GOOD TO GO FOR DOWNLOAD");
        }
        else if(response.length > 1 && response[0].equals("HTTP/1.1") && response[1].equals("404"))
        {
          println("No such file to download: " + fileToDownload +"!");
          fileSocket = null;
          gettingFileData = false;
          downloading = false;
          return;
        }
        else if(response[0].equals("Content-Length:") )
        { 
          bytesToRead = Integer.parseInt(response[1]);
          bytesRead = 0;
          //println("Content size:" + bytesToRead);
        }
      }
      else
      {
        byte[] read = new byte[5000];
        int justRead = fileSocket.readBytes(read);
        bytesRead += justRead;
        try { 
          imageFile.write(read, 0, justRead);
        }
        catch (IOException e) { 
          println("Something went horribly wrong.");
        }
        //println("Bytes read:" + bytesRead + " Bytes left:" + (bytesToRead - bytesRead));
        if(bytesRead == bytesToRead)
        {      
          try {  
            imageFile.flush();
            imageFile.close();
          } 
          catch (IOException e) { 
            println("Something went horribly wrong.");
          }
          fileSocket = null;
          println("Downloaded " + fileToDownload +"!");
          gettingFileData = false;
          downloading = false;
          downloadedEntries.add((ImageEntry)entries.get(toDownload.poll()));
        }
      }
    }
  }

  private void receiveData()
  {
    if(dataSocket == null)
      return;

    if (dataSocket.available() > 0) {      
      gettingData = true;
      data += dataSocket.readString(); 
      if(data.indexOf("]") != -1)
      {
        parse(data);
        data = "";
        gettingData = false;
        dataSocket = null;
        //println("Update Complete!");
      }
    }
  }

  private void parse(String data)
  {
    String[] parts = splitTokens(data, "{}");

    for(int i = 1; i < parts.length-1; i+=2)
    {
      parts[i] = trim(parts[i]);
      String[] items = splitTokens(parts[i], ",");

      ImageEntry imageEntry = new ImageEntry();
      for(int j = 0; j < items.length; ++j)
      {
        String[] value = splitTokens(items[j],"\"");
        if(value.length == 3)
        {
          if(value[0].equals("id"))
            imageEntry.setId(value[2]);
          else if(value[0].equals("fileName"))
            imageEntry.setFileName(value[2]);
          else if(value[0].equals("uploadTime"))
            imageEntry.setUploadTime(value[2]);
          else if(value[0].equals("rateAudio"))
            imageEntry.setRateAudio(value[2]);
          else if(value[0].equals("rateVideo"))
            imageEntry.setRateVideo(value[2]);
          else if(value[0].equals("rateTotal"))
            imageEntry.setRateTotal(value[2]);
          else if(value[0].equals("viewCount"))
            imageEntry.setViewCount(value[2]);
        }
      }
      if(!entries.containsKey(imageEntry.id))
      {
        entries.put(imageEntry.id, imageEntry);
        if(imageEntry.isDownloaded())
          downloadedEntries.add(imageEntry);
      }
      if(!imageEntry.isDownloaded())
      {
        toDownload.add(imageEntry.id);
      }
    }
  }
}

